﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;

#pragma warning disable 0618

namespace PP.Basic
{
    public static partial class TransformExtension
    {
        public static void SetActiveSelf(this Transform trans, bool active)
        {
            trans?.gameObject.SetActiveSelf(active);
        }
        public static void SetActiveEx(this Transform trans, bool active, bool isRecursive = true)
        {
            if (trans == null)
                return;
            if (isRecursive)
                trans.gameObject.SetActiveRecursively(active);
            else
                trans.gameObject.SetActiveSelf(active);
        }

        /// <summary>
        /// Set the layer to the given object and the full hierarchy below it.
        /// </summary>
        /// <param name="go">Start point of the traverse</param>
        /// <param name="layer">The layer to apply</param>
        /// <param name="isSelfIncluded">Indicates whether go should be set or not</param>
        public static void SetLayerRecursively(this Transform trans, int layer, bool isSelfIncluded = false)
        {
            if (trans == null)
                return;
            trans.gameObject.SetLayerRecursively(layer, isSelfIncluded);
        }

        public static void ActiveChild(this Transform parent, int index, bool active = true, bool isRecursive = false)
        {
            if (parent == null || parent.childCount <= 0 || index < 0 || index >= parent.childCount)
                return;

            if (isRecursive)
                parent.GetChild(index)?.gameObject.SetActiveRecursively(active);
            else
                parent.GetChild(index)?.gameObject.SetActiveSelf(active);
        }

        public static void ActiveChildren(this Transform parent, bool active = true, bool isRecursive = false)
        {
            if (parent == null)
                return;
            int childCount = parent.childCount;
            for (int i = childCount - 1; i >= 0; --i)
            {
                if (isRecursive)
                    parent.GetChild(i).gameObject.SetActiveRecursively(active);
                else
                    parent.GetChild(i).gameObject.SetActiveSelf(active);
            }
        }

        public static void DeactiveChild(this Transform parent, int index, bool isRecursive = false)
        {
            parent.ActiveChild(index, false, isRecursive);
        }

        public static void DeactiveChildren(this Transform parent, bool isRecursive = false)
        {
            parent.ActiveChildren(false, isRecursive);
        }

        public static void DestroyChildren(this Transform trans, bool immediate = false)
        {
            if (trans == null)
                return;

            for (int i = trans.childCount - 1; i >= 0; --i)
            {
                var go = trans.GetChild(i).gameObject;
                if (immediate)
                    UnityEngine.Object.DestroyImmediate(go);
                else
                    UnityEngine.Object.Destroy(go);
            }
        }

        public static void DestroyChildrenEx(this Transform trans, bool immediate = false)
        {
            if (trans == null)
                return;

#if UNITY_EDITOR
            Debug.LogWarning("This is a special method. Please make sure it's necessary to be called.");
#endif 
            for (int i = trans.childCount - 1; i >= 0; --i)
            {
                trans.GetChild(i).DestroyEx(immediate);
            }
        }

        public static void DestroyEx(this Transform trans, bool immediate = false)
        {
            if (trans == null)
                return;
            trans.gameObject.SetActiveRecursively(true);
            if (immediate)
            {
                GameObject.DestroyImmediate(trans.gameObject);
            }
            else
            {
                GameObject.Destroy(trans.gameObject);
            }
        }

        /// <summary>
        /// 销毁对象。查找所有DisposableBehaviour，调用Dispose方法，然后销毁GameObject。
        /// </summary>
        /// <param name="trans">是否需要销毁的节点，默认 true 销毁节点</param>
        /// <param name="isDestroyNow">调用OnDispose方法后，是否需要调用Destroy销毁此节点</param>
        public static void Dispose(this Transform trans, bool isDestroyNow = true)
        {
            if ((object)trans == null)
                return;
        }

        /// <summary>
        /// 销毁所有子节点
        /// </summary>
        /// <param name="trans">根节点</param>
        /// <param name="isDestroyNow">调用OnDispose方法后，是否需要调用Destroy销毁子节点，默认 true 销毁节点</param>
        /// <param name="immediate">是否立即销毁，Unity默认 false，不立即销毁</param>
        public static void DisposeChildren(this Transform trans, bool isDestroyNow = true, bool immediate = false)
        {
            if ((object)trans == null)
                return;
        }

        /// <summary>
        /// 销毁所有其它子节点
        /// </summary>
        /// <param name="trans">根节点</param>
        /// <param name="targetChildTrans">需要保留的子节点</param>
        /// <param name="isDestroyNow">调用OnDispose方法后，是否需要调用Destroy销毁子节点，默认 true 销毁节点</param>
        /// <param name="immediate">是否立即销毁，Unity默认 false，不立即销毁</param>
        public static void DisposeOtherChildren(this Transform trans, Transform targetChildTrans, bool isDestroyNow = true, bool immediate = false)
        {
            if ((object)trans == null)
                return;

            for (int i = trans.childCount - 1; i >= 0; --i)
            {
                var childTrans = trans.GetChild(i);
                if (childTrans == targetChildTrans)
                    continue;
            }
        }

        public static Component AddComponentEx(this Transform trans, Type t)
        {
            if (trans == null || t == null)
                return null;
            Component o = trans.gameObject.GetComponent(t);
            if (o == null)
            {
                o = trans.gameObject.AddComponent(t);
            }
            return o;
        }

        public static T AddComponentEx<T>(this Transform trans) where T : Component
        {
            return trans.gameObject.AddComponentEx<T>();
        }

        /// <summary>
        /// 从transform上获取组件，如果获取不到，就AddComponent。
        /// </summary>
        /// <example>trans.GetComponentEx<Image>();</Image></example>
        public static T GetComponentEx<T>(this Transform trans) where T : Component
        {
            return trans.gameObject.GetComponentEx<T>();
        }

        public static Vector2 RectTransformSizeEx(this Transform trans)
        {
            Vector2 size = Vector2.zero;
            do
            {
                if (trans == null)
                {
                    break;
                }
                var rectTransform = trans.GetComponent<RectTransform>();
                if (rectTransform == null)
                {
                    break;
                }
                size = rectTransform.rect.size;
                if (size != Vector2.zero)
                {
                    break;
                }
                var layoutElement = rectTransform.GetComponent<LayoutElement>();
                if (layoutElement == null)
                {
                    break;
                }
                size = new Vector2(layoutElement.preferredWidth, layoutElement.preferredHeight);
            } while (false);
            return size;
        }

        /// <summary>
        /// 将fromTrans移动到toTrans的世界位置
        /// 同时，将fromTrans的所有子孙节点移动到toTrans的所有子孙节点的本地相对位置
        /// fromTrans和toTrans的节点结构必须一致
        /// </summary>
        /// <param name="fromTrans">需要移动的根节点</param>
        /// <param name="toTrans">目的地的根节点</param>
        /// <param name="duration">动画时间</param>
        public static void SetPositionToMatch(this Transform fromTrans, Transform toTrans)
        {
            if (fromTrans.position != toTrans.position)
            {
                // 使用世界坐标移动根节点
                fromTrans.position = toTrans.position;
            }
            SetChildrenPositionToMatch(fromTrans, toTrans);
        }

        /// <summary>
        /// 将fromTrans的所有子孙节点移动到toTrans的所有子孙节点的本地相对位置
        /// fromTrans和toTrans的节点结构必须一致。
        /// </summary>
        /// <param name="fromTrans">需要移动的根节点</param>
        /// <param name="toTrans">目的地的根节点</param>
        /// <param name="duration">动画时间</param>
        public static void SetChildrenPositionToMatch(this Transform fromTrans, Transform toTrans)
        {
            for (int i = 0; i < fromTrans.childCount; i++)
            {
                Transform childFrom = fromTrans.GetChild(i);
                Transform childTo = toTrans.GetChild(i);

                if (childFrom.localPosition != childTo.localPosition)
                {
                    childFrom.localPosition = childTo.localPosition;
                }

                // 递归处理更深层的子孙节点
                SetChildrenPositionToMatch(childFrom, childTo);
            }
        }

        private static void GetHierPath(StringBuilder stringBuilder, Transform transform, string delimiter, string prefix)
        {
            // 获取父节点的名字
            if (transform.parent != null)
            {
                GetHierPath(stringBuilder, transform.parent, delimiter, prefix);
                stringBuilder.Append(delimiter);
            }
            else
            {
                // 到达根节点了
                if (!string.IsNullOrEmpty(prefix))
                {
                    stringBuilder.Append(prefix);
                }
            }

            // 记录当前节点名字
            stringBuilder.Append(transform.name);
        }

        public static void WalkThrough(this Transform trans, Action<Transform> action, bool isSelfIncluded = false)
        {
            if (!trans || action == null)
                return;

            if (isSelfIncluded)
            {
                action(trans);
            }

            foreach (var child in trans.EnumerateHierarchy())
            {
                action(child);
            }
        }

        /// <summary>
        /// Enumerates all children in the hierarchy starting at the root object.
        /// </summary>
        /// <param name="root">Start point of the traversion set</param>
        public static IEnumerable<Transform> EnumerateHierarchy(this Transform root)
        {
            if (root == null) { throw new ArgumentNullException("root"); }
            return root.EnumerateHierarchyCore(new List<Transform>(0));
        }

        /// <summary>
        /// Enumerates all children in the hierarchy starting at the root object except for the branches in ignore.
        /// </summary>
        /// <param name="root">Start point of the traversion set</param>
        /// <param name="ignore">Transforms and all its children to be ignored</param>
        public static IEnumerable<Transform> EnumerateHierarchy(this Transform root, ICollection<Transform> ignore)
        {
            if (root == null) { throw new ArgumentNullException("root"); }
            if (ignore == null)
            {
                throw new ArgumentNullException("ignore", "Ignore collection can't be null, use EnumerateHierarchy(root) instead.");
            }
            return root.EnumerateHierarchyCore(ignore);
        }

        /// <summary>
        /// Enumerates all children in the hierarchy starting at the root object except for the branches in ignore.
        /// </summary>
        /// <param name="root">Start point of the traversion set</param>
        /// <param name="ignore">Transforms and all its children to be ignored</param>
        private static IEnumerable<Transform> EnumerateHierarchyCore(this Transform root, ICollection<Transform> ignore)
        {
            var transformQueue = new Queue<Transform>();
            for (var i = 0; i < root.childCount; i++)
            {
                transformQueue.Enqueue(root.GetChild(i));
            }

            while (transformQueue.Count > 0)
            {
                var parentTransform = transformQueue.Dequeue();

                if (!parentTransform || ignore.Contains(parentTransform)) { continue; }

                for (var i = 0; i < parentTransform.childCount; i++)
                {
                    transformQueue.Enqueue(parentTransform.GetChild(i));
                }

                yield return parentTransform;
            }
        }
    }
}
